home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / libwww2 / HTFile.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  30.9 KB  |  1,297 lines

  1. /*            File Access                HTFile.c
  2. **            ===========
  3. **
  4. **    This is unix-specific code in general, with some VMS bits.
  5. **    These are routines for file access used by browsers.
  6. **
  7. ** History:
  8. **       Feb 91    Written Tim Berners-Lee CERN/CN
  9. **       Apr 91    vms-vms access included using DECnet syntax
  10. **    26 Jun 92 (JFG) When running over DECnet, suppressed FTP.
  11. **            Fixed access bug for relative names on VMS.
  12. **
  13. ** Bugs:
  14. **    FTP: Cannot access VMS files from a unix machine.
  15. **    How can we know that the
  16. **    target machine runs VMS?
  17. */
  18.  
  19. #include "HTFile.h"             /* Implemented here */
  20.  
  21. #define INFINITY 512        /* file name length @@ FIXME */
  22. #define MULTI_SUFFIX ".multi"   /* Extension for scanning formats */
  23.  
  24. #include <stdio.h>
  25. #include <sys/param.h>
  26. #include "HText.h"
  27. #include "HTUtils.h"
  28.  
  29. #include "HTParse.h"
  30. #include "tcp.h"
  31. #include "HTTCP.h"
  32. #include "HTFTP.h"
  33. #include "HTAnchor.h"
  34. #include "HTAtom.h"
  35. #include "HTWriter.h"
  36. #include "HTFWriter.h"
  37. #include "HTInit.h"
  38. #include "HTSort.h"
  39.  
  40. /* #define TRACE 1 */
  41.  
  42. typedef struct _HTSuffix {
  43.     char *        suffix;
  44.     HTAtom *    rep;
  45.     HTAtom *    encoding;
  46.     float        quality;
  47. } HTSuffix;
  48.  
  49.  
  50. #ifdef USE_DIRENT        /* Set this for Sys V systems */
  51. #define STRUCT_DIRENT struct dirent
  52. #else
  53. #define STRUCT_DIRENT struct direct
  54. #endif
  55.  
  56. #include "HTML.h"               /* For directory object building */
  57.  
  58. #define PUTC(c) (*target->isa->put_character)(target, c)
  59. #define PUTS(s) (*target->isa->put_string)(target, s)
  60. #define START(e) (*target->isa->start_element)(target, e, 0, 0)
  61. #define END(e) (*target->isa->end_element)(target, e)
  62. #define END_TARGET (*target->isa->end_document)(target)
  63. #define FREE_TARGET (*target->isa->free)(target)
  64. struct _HTStructured {
  65.     CONST HTStructuredClass *    isa;
  66.     /* ... */
  67. };
  68.  
  69. /* For, e.g., Solaris. */
  70. #ifndef NGROUPS
  71. #define NGROUPS 64
  72. #endif
  73.  
  74.  
  75. /*             Controlling globals
  76. **
  77. */
  78.  
  79. PUBLIC int HTDirAccess = HT_DIR_OK;
  80. PUBLIC int HTDirReadme = HT_DIR_README_TOP;
  81.  
  82. #ifdef vms
  83. PRIVATE char *HTCacheRoot = "/WWW$SCRATCH/";   /* Where to cache things */
  84. #else
  85. PRIVATE char *HTCacheRoot = "/tmp/W3_Cache_";   /* Where to cache things */
  86. #endif
  87.  
  88. /* PRIVATE char *HTSaveRoot  = "$(HOME)/WWW/";*/    /* Where to save things */
  89.  
  90.  
  91. /*    Suffix registration
  92. */
  93.  
  94. PUBLIC HTList * HTSuffixes = 0;
  95. PRIVATE HTSuffix no_suffix = { "*", NULL, NULL, 1.0 };
  96. PRIVATE HTSuffix unknown_suffix = { "*.*", NULL, NULL, 1.0};
  97.  
  98.  
  99. /*    Define the representation associated with a file suffix
  100. **    -------------------------------------------------------
  101. **
  102. **    Calling this with suffix set to "*" will set the default
  103. **    representation.
  104. **    Calling this with suffix set to "*.*" will set the default
  105. **    representation for unknown suffix files which contain a ".".
  106. */
  107. PUBLIC void HTSetSuffix ARGS4(
  108.     CONST char *,    suffix,
  109.     CONST char *,    representation,
  110.     CONST char *,    encoding,
  111.     float,        value)
  112. {
  113.  
  114.     HTSuffix * suff;
  115.  
  116.     if (strcmp(suffix, "*")==0) suff = &no_suffix;
  117.     else if (strcmp(suffix, "*.*")==0) suff = &unknown_suffix;
  118.     else {
  119.     suff = (HTSuffix*) calloc(1, sizeof(HTSuffix));
  120.     if (suff == NULL) outofmem(__FILE__, "HTSetSuffix");
  121.  
  122.     if (!HTSuffixes) HTSuffixes = HTList_new();
  123.     HTList_addObject(HTSuffixes, suff);
  124.  
  125.     StrAllocCopy(suff->suffix, suffix);
  126.     }
  127.  
  128.     suff->rep = HTAtom_for(representation);
  129.  
  130.     {
  131.     char *enc = NULL, *p;
  132.     StrAllocCopy(enc, encoding);
  133.     for (p=enc; *p; p++) *p = TOLOWER(*p);
  134.     suff->encoding = HTAtom_for(enc);
  135.     free (enc);
  136.     }
  137.  
  138.     suff->quality = value;
  139. }
  140.  
  141.  
  142.  
  143.  
  144. #ifdef vms
  145. /*    Convert unix-style name into VMS name
  146. **    -------------------------------------
  147. **
  148. ** Bug: Returns pointer to static -- non-reentrant
  149. */
  150. PRIVATE char * vms_name(CONST char * nn, CONST char * fn)
  151. {
  152.  
  153. /*    We try converting the filename into Files-11 syntax. That is, we assume
  154. **    first that the file is, like us, on a VMS node. We try remote
  155. **    (or local) DECnet access. Files-11, VMS, VAX and DECnet
  156. **    are trademarks of Digital Equipment Corporation.
  157. **    The node is assumed to be local if the hostname WITHOUT DOMAIN
  158. **    matches the local one. @@@
  159. */
  160.     static char vmsname[INFINITY];    /* returned */
  161.     char * filename = (char*)malloc(strlen(fn)+1);
  162.     char * nodename = (char*)malloc(strlen(nn)+2+1);    /* Copies to hack */
  163.     char *second;        /* 2nd slash */
  164.     char *last;         /* last slash */
  165.  
  166.     char * hostname = HTHostName();
  167.  
  168.     if (!filename || !nodename) outofmem(__FILE__, "vms_name");
  169.     strcpy(filename, fn);
  170.     strcpy(nodename, "");       /* On same node? Yes if node names match */
  171.     {
  172.     char *p, *q;
  173.     for (p=hostname, q=nn; *p && *p!='.' && *q && *q!='.'; p++, q++){
  174.         if (TOUPPER(*p)!=TOUPPER(*q)) {
  175.         strcpy(nodename, nn);
  176.         q = strchr(nodename, '.');      /* Mismatch */
  177.         if (q) *q=0;            /* Chop domain */
  178.         strcat(nodename, "::");         /* Try decnet anyway */
  179.         break;
  180.         }
  181.     }
  182.     }
  183.  
  184.     second = strchr(filename+1, '/');           /* 2nd slash */
  185.     last = strrchr(filename, '/');      /* last slash */
  186.  
  187.     if (!second) {                /* Only one slash */
  188.     sprintf(vmsname, "%s%s", nodename, filename + 1);
  189.     } else if(second==last) {        /* Exactly two slashes */
  190.     *second = 0;        /* Split filename from disk */
  191.     sprintf(vmsname, "%s%s:%s", nodename, filename+1, second+1);
  192.     *second = '/';  /* restore */
  193.     } else {                /* More than two slashes */
  194.     char * p;
  195.     *second = 0;        /* Split disk from directories */
  196.     *last = 0;        /* Split dir from filename */
  197.     sprintf(vmsname, "%s%s:[%s]%s",
  198.         nodename, filename+1, second+1, last+1);
  199.     *second = *last = '/';  /* restore filename */
  200.     for (p=strchr(vmsname, '['); *p!=']'; p++)
  201.         if (*p=='/') *p='.';        /* Convert dir sep.  to dots */
  202.     }
  203.     free(nodename);
  204.     free(filename);
  205.     return vmsname;
  206. }
  207.  
  208.  
  209. #endif /* vms */
  210.  
  211.  
  212.  
  213. /*    Send README file
  214. **
  215. **  If a README file exists, then it is inserted into the document here.
  216. */
  217.  
  218. #ifdef GOT_READ_DIR
  219. PRIVATE void do_readme ARGS2(HTStructured *, target, CONST char *, localname)
  220. {
  221.     FILE * fp;
  222.     char * readme_file_name =
  223.     malloc(strlen(localname)+ 1 + strlen(HT_DIR_README_FILE) + 1);
  224.     strcpy(readme_file_name, localname);
  225.     strcat(readme_file_name, "/");
  226.     strcat(readme_file_name, HT_DIR_README_FILE);
  227.  
  228.     fp = fopen(readme_file_name,  "r");
  229.  
  230.     if (fp) {
  231.     HTStructuredClass targetClass;
  232.  
  233.     targetClass =  *target->isa;    /* (Can't init agregate in K&R) */
  234.     START(HTML_PRE);
  235.     for(;;){
  236.         char c = fgetc(fp);
  237.         if (c == (char)EOF) break;
  238.         switch (c) {
  239.         case '&':
  240.         case '<':
  241.         case '>':
  242.             PUTC('&');
  243.             PUTC('#');
  244.             PUTC((char)(c / 10));
  245.             PUTC((char) (c % 10));
  246.             PUTC(';');
  247.             break;
  248. /*        case '\n':
  249.             PUTC('\r');
  250. Bug removed thanks to joe@athena.mit.edu */
  251.         default:
  252.             PUTC(c);
  253.         }
  254.     }
  255.     END(HTML_PRE);
  256.     fclose(fp);
  257.     }
  258. }
  259. #endif
  260.  
  261.  
  262. /*    Make the cache file name for a W3 document
  263. **    ------------------------------------------
  264. **    Make up a suitable name for saving the node in
  265. **
  266. **    E.g.    /tmp/WWW_Cache_news/1234@cernvax.cern.ch
  267. **        /tmp/WWW_Cache_http/crnvmc/FIND/xx.xxx.xx
  268. **
  269. ** On exit,
  270. **    returns a malloc'ed string which must be freed by the caller.
  271. */
  272. PUBLIC char * HTCacheFileName ARGS1(CONST char *,name)
  273. {
  274.     char * access = HTParse(name, "", PARSE_ACCESS);
  275.     char * host = HTParse(name, "", PARSE_HOST);
  276.     char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
  277.  
  278.     char * result;
  279.     result = (char *)malloc(
  280.         strlen(HTCacheRoot)+strlen(access)
  281.         +strlen(host)+strlen(path)+6+1);
  282.     if (result == NULL) outofmem(__FILE__, "HTCacheFileName");
  283.     sprintf(result, "%s/WWW/%s/%s%s", HTCacheRoot, access, host, path);
  284.     free(path);
  285.     free(access);
  286.     free(host);
  287.     return result;
  288. }
  289.  
  290.  
  291. /*    Open a file for write, creating the path
  292. **    ----------------------------------------
  293. */
  294. #ifdef NOT_IMPLEMENTED
  295. PRIVATE int HTCreatePath ARGS1(CONST char *,path)
  296. {
  297.     return -1;
  298. }
  299. #endif
  300.  
  301. /*    Convert filenames between local and WWW formats
  302. **    -----------------------------------------------
  303. ** On exit,
  304. **    returns a malloc'ed string which must be freed by the caller.
  305. */
  306. PUBLIC char * HTLocalName ARGS1(CONST char *,name)
  307. {
  308.   char * access = HTParse(name, "", PARSE_ACCESS);
  309.   char * host = HTParse(name, "", PARSE_HOST);
  310.   char * path = HTParse(name, "", PARSE_PATH+PARSE_PUNCTUATION);
  311.  
  312.   HTUnEscape(path);    /* Interpret % signs */
  313.  
  314.   #if _AMIGA
  315.    /* Amiga files are stored as volume:file instead of /volume/file
  316.     *  or whatever. This little bit of code massages the string
  317.     *    appropriately */
  318.    if (path &&           /* path was specified */
  319.        *path == '/' &&     /* top of a volume */
  320.        strchr (path, ':')) /* amiga volume name present */
  321.    {
  322.       char *newpath = strdup (path + 1);
  323.  
  324.       if (newpath)
  325.       {
  326.      free (path);
  327.      path = newpath;
  328.       }
  329.    }
  330.   #endif
  331.  
  332.   if (0==strcmp(access, "file"))
  333.     {
  334.       free(access);
  335.       if (!host || !*host || (0==strcasecomp(host, HTHostName())) ||
  336.       (0==strcasecomp(host, "localhost")))
  337.     {
  338.       if (host)
  339.         free(host);
  340.       if (TRACE)
  341.         fprintf(stderr, "Node `%s' means path `%s'\n", name, path);
  342.       return(path);
  343.     }
  344.       else
  345.     {
  346.       free (host);
  347.       if (path)
  348.         free (path);
  349.       return NULL;
  350.     }
  351.     }
  352.  
  353.   /* not file */
  354.   if (host)
  355.     free (host);
  356.   free (access);
  357.   if (path)
  358.     free (path);
  359.   return NULL;
  360. }
  361.  
  362.  
  363. /*    Make a WWW name from a full local path name
  364. **
  365. ** Bugs:
  366. **    At present, only the names of two network root nodes are hand-coded
  367. **    in and valid for the NeXT only. This should be configurable in
  368. **    the general case.
  369. */
  370.  
  371. PUBLIC char * WWW_nameOfFile ARGS1 (CONST char *,name)
  372. {
  373.     char * result;
  374.     result = (char *)malloc(7+strlen(HTHostName())+strlen(name)+1);
  375.     if (result == NULL) outofmem(__FILE__, "WWW_nameOfFile");
  376.     sprintf(result, "file://%s%s", HTHostName(), name);
  377.     if (TRACE) fprintf(stderr, "File `%s'\n\tmeans node `%s'\n", name, result);
  378.     return result;
  379. }
  380.  
  381.  
  382. /*    Determine a suitable suffix, given the representation
  383. **    -----------------------------------------------------
  384. **
  385. ** On entry,
  386. **    rep    is the atomized MIME style representation
  387. **
  388. ** On exit,
  389. **    returns a pointer to a suitable suffix string if one has been
  390. **        found, else "".
  391. */
  392. PUBLIC CONST char * HTFileSuffix ARGS1(HTAtom*, rep)
  393. {
  394.     HTSuffix * suff;
  395.     int n;
  396.     int i;
  397.  
  398.     if (!HTSuffixes) HTFileInit();
  399.     n = HTList_count(HTSuffixes);
  400.     for(i=0; i<n; i++) {
  401.     suff = HTList_objectAt(HTSuffixes, i);
  402.     if (suff->rep == rep) {
  403.         return suff->suffix;        /* OK -- found */
  404.     }
  405.     }
  406.     return "";          /* Dunno */
  407. }
  408.  
  409.  
  410. /*    Determine file format from file name
  411. **    ------------------------------------
  412. **
  413. **    This version will return the representation and also set
  414. **    a variable for the encoding.
  415. **
  416. **    It will handle for example  x.txt, x.txt.Z, x.Z
  417. */
  418.  
  419. PUBLIC HTFormat HTFileFormat ARGS4 (
  420.             char *, filename,
  421.             HTAtom **,    pencoding,
  422.             HTAtom *,    default_type,
  423.             int *, compressed)
  424. {
  425.   HTSuffix *suff;
  426.   int n, i, lf;
  427.  
  428.   if (!filename)
  429.     return NULL;
  430.  
  431.   /* Make a copy to hack and slash. */
  432.   filename = strdup (filename);
  433.  
  434.   lf = strlen (filename);
  435.  
  436.   /* Step backward through filename, looking for '?'. */
  437.   for (i = lf - 1; i >= 0; i--)
  438.     {
  439.       if (filename[i] == '?')
  440.     {
  441.       /* Clip query. */
  442.       filename[i] = '\0';
  443.       /* Get new strlen, since we just changed it. */
  444.       lf = strlen (filename);
  445.       goto ok_ready;
  446.     }
  447.     }
  448.  
  449.   *compressed = 0;
  450.  
  451.   /* Check for .Z and .z. */
  452.   if (lf > 2)
  453.     {
  454.       if (strcmp (&(filename[lf-2]), ".Z") == 0)
  455.     {
  456.       *compressed = COMPRESSED_BIGZ;
  457.       filename[lf-2] = '\0';
  458.       lf = strlen (filename);
  459.       if (TRACE)
  460.         fprintf (stderr, "[HTFileFormat] Got hit on .Z; filename '%s'\n",
  461.              filename);
  462.       goto ok_ready;
  463.     }
  464.       else if (strcmp (&(filename[lf-2]), ".z") == 0)
  465.     {
  466.       *compressed = COMPRESSED_GNUZIP;
  467.       filename[lf-2] = '\0';
  468.       lf = strlen (filename);
  469.       if (TRACE)
  470.         fprintf (stderr, "[HTFileFormat] Got hit on .z; filename '%s'\n",
  471.              filename);
  472.       goto ok_ready;
  473.     }
  474.       else if (lf > 3)
  475.     {
  476.       if (strcmp (&(filename[lf-3]), ".gz") == 0)
  477.         {
  478.           *compressed = COMPRESSED_GNUZIP;
  479.           filename[lf-3] = '\0';
  480.           lf = strlen (filename);
  481.           if (TRACE)
  482.         fprintf (stderr,
  483.              "[HTFileFormat] Got hit on .gz; filename '%s'\n",
  484.              filename);
  485.           goto ok_ready;
  486.         }
  487.     }
  488.     }
  489.  
  490.  ok_ready:
  491.   if (!HTSuffixes)
  492.     HTFileInit();
  493.  
  494.   *pencoding = NULL;
  495.  
  496.   n = HTList_count(HTSuffixes);
  497.  
  498.   for(i=0; i<n; i++)
  499.     {
  500.       int ls;
  501.       suff = HTList_objectAt(HTSuffixes, i);
  502.       ls = strlen(suff->suffix);
  503.       if ((ls <= lf) && 0==strcasecomp(suff->suffix, filename + lf - ls))
  504.     {
  505.       int j;
  506.       *pencoding = suff->encoding;
  507.       if (suff->rep)
  508.         goto done;
  509.  
  510.       for(j=0; j<n; j++)
  511.         {  /* Got encoding, need representation */
  512.           int ls2;
  513.           suff = HTList_objectAt(HTSuffixes, j);
  514.           ls2 = strlen(suff->suffix);
  515.           if ((ls <= lf) &&
  516.           0==strncasecomp(suff->suffix, filename + lf - ls -ls2, ls2))
  517.         if (suff->rep)
  518.           goto done;
  519.         }
  520.     }
  521.     }
  522.  
  523.   suff = strchr(filename, '.') ?        /* Unknown suffix */
  524.     ( unknown_suffix.rep ? &unknown_suffix : &no_suffix)
  525.       : &no_suffix;
  526.  
  527.   /* For now, assuming default is 8bit text/plain.
  528.      We also want default 8bit text/html for http connections. */
  529.  
  530.   /* set default encoding unless found with suffix already */
  531.   if (!*pencoding) *pencoding = suff->encoding ? suff->encoding
  532.     : HTAtom_for("8bit");
  533.  
  534.  done:
  535.  
  536.   /* Free our copy. */
  537.   free (filename);
  538.   return suff->rep ? suff->rep : default_type;
  539. }
  540.  
  541.  
  542. /*    Determine file format from file name -- string version
  543. **    ------------------------------------------------------
  544. */
  545. PUBLIC char *HTFileMimeType ARGS2 (
  546.             CONST char *,    filename,
  547.             CONST char *,    default_type)
  548. {
  549.   HTAtom *pencoding;
  550.   HTFormat format;
  551.   int compressed;
  552.  
  553.   format = HTFileFormat (filename, &pencoding, HTAtom_for (default_type),
  554.              &compressed);
  555.  
  556.   if (HTAtom_name (format))
  557.     return HTAtom_name (format);
  558.   else
  559.     return default_type;
  560. }
  561.  
  562. /* This doesn't do Gopher typing yet. */
  563. /* This assumes we get a canonical URL and that HTParse works. */
  564. char *HTDescribeURL (char *url)
  565. {
  566.   char line[512];
  567.   char *type, *t, *st = NULL;
  568.   char *host;
  569.   char *access;
  570.   int i;
  571.  
  572.   if (!url || !*url)
  573.     return "Completely content-free.";
  574.  
  575.   if (strncmp ("http:", url, 5) == 0)
  576.     type = HTFileMimeType (url, "text/html");
  577.   else
  578.     type = HTFileMimeType (url, "text/plain");
  579.  
  580.   if (TRACE)
  581.     fprintf (stderr, "DESCRIBE: type '%s'\n", type);
  582.  
  583.   t = strdup (type);
  584.   for (i = 0; i < strlen (t); i++)
  585.     {
  586.       if (t[i] == '/')
  587.     {
  588.       t[i] = '\0';
  589.       if (t[i+1] != '\0' && t[i+1] != '*')
  590.         st = &(t[i+1]);
  591.       goto got_subtype;
  592.     }
  593.     }
  594.  got_subtype:
  595.  
  596.   access = HTParse (url, "", PARSE_ACCESS);
  597.   if (strcmp (access, "http") == 0)
  598.     {
  599.       access[0] = 'H';
  600.       access[1] = 'T';
  601.       access[2] = 'T';
  602.       access[3] = 'P';
  603.     }
  604.   else if (strcmp (access, "ftp") == 0)
  605.     {
  606.       access[0] = 'F';
  607.       access[1] = 'T';
  608.       access[2] = 'P';
  609.     }
  610.   else
  611.     {
  612.       access[0] = toupper(access[0]);
  613.     }
  614.  
  615.   if (TRACE)
  616.     fprintf (stderr, "DESCRIBE: url '%s'\n", url);
  617.  
  618.   host = HTParse (url, "", PARSE_HOST);
  619.  
  620.   if (TRACE)
  621.     fprintf (stderr, "DESCRIBE: host '%s'\n", host);
  622.  
  623. #if 0
  624.   for (i = 0; i < strlen (host); i++)
  625.     if (host[i] == ':')
  626.       host[i] = '\0';
  627. #endif
  628.  
  629.   if (st)
  630.     {
  631.       /* Uppercase type, to start sentence. */
  632.       t[0] = toupper(t[0]);
  633.       /* Crop x- from subtype. */
  634.       if (st[0] == 'x' && st[1] == '-')
  635.     st = &(st[2]);
  636.       if (TRACE)
  637.     fprintf (stderr,
  638.          "DESCRIBE: in if (st); pasting together %s %s %s %s %s\n",
  639.          t,
  640.          (strcmp (t, "Application") == 0 ? " data" : ""),
  641.          st, host, access);
  642.       sprintf (line, "%s%s, type %s, on host %s, via %s.",
  643.            t,
  644.            (strcmp (t, "Application") == 0 ? " data" : ""),
  645.            st, host, access);
  646.       if (TRACE)
  647.     fprintf (stderr, "DESCRIBE: pasted together '%s'\n", line);
  648.     }
  649.   else
  650.     {
  651.       sprintf (line, "Type %s, on host %s, via %s.", type, host, access);
  652.       if (TRACE)
  653.     fprintf (stderr, "DESCRIBE: pasted together '%s'\n", line);
  654.     }
  655.  
  656.   free (access);
  657.   free (host);
  658.   free (t);
  659.  
  660.   if (TRACE)
  661.     fprintf (stderr, "DESCRIBE: returning '%s'\n", line);
  662.  
  663.   return strdup (line);
  664. }
  665.  
  666.  
  667. /*    Determine value from file name
  668. **    ------------------------------
  669. **
  670. */
  671.  
  672. PUBLIC float HTFileValue ARGS1 (CONST char *,filename)
  673.  
  674. {
  675.     HTSuffix * suff;
  676.     int n;
  677.     int i;
  678.     int lf = strlen(filename);
  679.  
  680.     if (!HTSuffixes) HTFileInit();
  681.     n = HTList_count(HTSuffixes);
  682.     for(i=0; i<n; i++) {
  683.     int ls;
  684.     suff = HTList_objectAt(HTSuffixes, i);
  685.     ls = strlen(suff->suffix);
  686.     if ((ls <= lf) && 0==strcmp(suff->suffix, filename + lf - ls)) {
  687.         if (TRACE) fprintf(stderr, "File: Value of %s is %.3f\n",
  688.                    filename, suff->quality);
  689.         return suff->quality;        /* OK -- found */
  690.     }
  691.     }
  692.     return 0.3;     /* Dunno! */
  693. }
  694.  
  695.  
  696. /*    Determine write access to a file
  697. **    --------------------------------
  698. **
  699. ** On exit,
  700. **    return value    YES if file can be accessed and can be written to.
  701. **
  702. ** Bugs:
  703. **    1.    No code for non-unix systems.
  704. **    2.    Isn't there a quicker way?
  705. */
  706.  
  707. #ifdef vms
  708. #define NO_GROUPS
  709. #endif
  710. #ifdef _AMIGA
  711. /* AS225r2 does have groups -- FIXME later */
  712. #define NO_GROUPS
  713. #endif
  714. #ifdef NO_UNIX_IO
  715. #define NO_GROUPS
  716. #endif
  717. #ifdef PCNFS
  718. #define NO_GROUPS
  719. #endif
  720.  
  721. PUBLIC BOOL HTEditable ARGS1 (CONST char *,filename)
  722. {
  723. #ifdef NO_GROUPS
  724.     return NO;        /* Safe answer till we find the correct algorithm */
  725. #else
  726.     int     groups[NGROUPS];
  727.     uid_t    myUid;
  728.     int     ngroups;            /* The number of groups  */
  729.     struct stat fileStatus;
  730.     int     i;
  731.  
  732.     if (stat(filename, &fileStatus))        /* Get details of filename */
  733.     return NO;                /* Can't even access file! */
  734.  
  735.     /* The group stuff appears to be coming back garbage on IRIX... why? */
  736.     ngroups = getgroups(NGROUPS, groups);    /* Groups to which I belong  */
  737.     myUid = geteuid();                /* Get my user identifier */
  738.  
  739.     if (TRACE) {
  740.     int i;
  741.     fprintf(stderr,
  742.         "File mode is 0%o, uid=%d, gid=%d. My uid=%d, %d groups (",
  743.         (unsigned int) fileStatus.st_mode, fileStatus.st_uid,
  744.         fileStatus.st_gid,
  745.         myUid, ngroups);
  746.     for (i=0; i<ngroups; i++) fprintf(stderr, " %d", groups[i]);
  747.     fprintf(stderr, ")\n");
  748.     }
  749.  
  750.     if (fileStatus.st_mode & 0002)        /* I can write anyway? */
  751.     return YES;
  752.  
  753.     if ((fileStatus.st_mode & 0200)        /* I can write my own file? */
  754.      && (fileStatus.st_uid == myUid))
  755.     return YES;
  756.  
  757.     if (fileStatus.st_mode & 0020)        /* Group I am in can write? */
  758.     {
  759.     for (i=0; i<ngroups; i++) {
  760.         if (groups[i] == fileStatus.st_gid)
  761.         return YES;
  762.     }
  763.     }
  764.     if (TRACE) fprintf(stderr, "\tFile is not editable.\n");
  765.     return NO;                    /* If no excuse, can't do */
  766. #endif
  767. }
  768.  
  769.  
  770.  
  771. /*    Output one directory entry
  772. **
  773. */
  774. PUBLIC void HTDirEntry ARGS3(HTStructured *, target,
  775.          CONST char * , tail,
  776.          CONST char *,    entry)
  777. {
  778.     char * relative;
  779.     char * escaped = HTEscape(entry);
  780.  
  781.     /* If empty tail, gives absolute ref below */
  782.     relative = (char*) malloc(strlen(tail) + strlen(escaped)+2);
  783.     sprintf(relative, "%s/%s", tail, escaped);
  784.     PUTS("<A HREF=\"");
  785.     PUTS(relative);
  786.     PUTS("\">");
  787.     free(escaped);
  788.     free(relative);
  789.     PUTS(entry);
  790.     PUTS("</A>");
  791. }
  792.  
  793. /*    Output parent directory entry
  794. **
  795. **    This gives the TITLE and H1 header, and also a link
  796. **    to the parent directory if appropriate.
  797. */
  798. PUBLIC void HTDirTitles ARGS2(HTStructured *, target,
  799.          HTAnchor * , anchor)
  800.  
  801. {
  802.     char * logical = HTAnchor_address(anchor);
  803.     char * path = HTParse(logical, "", PARSE_PATH + PARSE_PUNCTUATION);
  804.     char * current;
  805.  
  806.     current = strrchr(path, '/');       /* last part or "" */
  807.     free(logical);
  808.  
  809.     {
  810.       char * printable = NULL;
  811.       StrAllocCopy(printable, (current + 1));
  812.       HTUnEscape(printable);
  813.       START(HTML_TITLE);
  814.       PUTS(*printable ? printable : "Welcome ");
  815.       PUTS(" directory");
  816.       END(HTML_TITLE);
  817.  
  818.       START(HTML_H1);
  819.       PUTS(*printable ? printable : "Welcome");
  820.       END(HTML_H1);
  821.       free(printable);
  822.     }
  823.  
  824.     /*    Make link back to parent directory
  825.      */
  826.  
  827.     if (current && current[1]) {   /* was a slash AND something else too */
  828.     char * parent;
  829.     char * relative;
  830.     *current++ = 0;
  831.       parent = strrchr(path, '/');  /* penultimate slash */
  832.  
  833.     relative = (char*) malloc(strlen(current) + 4);
  834.     if (relative == NULL) outofmem(__FILE__, "DirRead");
  835.     sprintf(relative, "%s/..", current);
  836.     PUTS ("<A HREF=\"");
  837.     PUTS (relative);
  838.     PUTS ("\">");
  839.     free(relative);
  840.  
  841.     PUTS("Up to ");
  842.     if (parent) {
  843.       char * printable = NULL;
  844.       StrAllocCopy(printable, parent + 1);
  845.       HTUnEscape(printable);
  846.       PUTS(printable);
  847.       free(printable);
  848.     } else {
  849.       PUTS("/");
  850.     }
  851.  
  852.     PUTS("</A>");
  853.       }
  854.     free(path);
  855. }
  856.  
  857.  
  858.  
  859. /*    Load a document
  860. **    ---------------
  861. **
  862. ** On entry,
  863. **    addr        must point to the fully qualified hypertext reference.
  864. **            This is the physsical address of the file
  865. **
  866. ** On exit,
  867. **    returns     <0        Error has occured.
  868. **            HTLOADED    OK
  869. **
  870. */
  871. PUBLIC int HTLoadFile ARGS4 (
  872.     CONST char *,        addr,
  873.     HTParentAnchor *,    anchor,
  874.     HTFormat,        format_out,
  875.     HTStream *,        sink
  876. )
  877. {
  878.     char * filename;
  879.     HTFormat format;
  880.     int fd = -1;        /* Unix file descriptor number = INVALID */
  881.     char * nodename = 0;
  882.     char * newname=0;    /* Simplified name of file */
  883.     HTAtom * encoding;    /* @@ not used yet */
  884.     int compressed;
  885.     extern char *HTgeticonname(HTFormat, char *);
  886.  
  887. /*    Reduce the filename to a basic form (hopefully unique!)
  888. */
  889.     StrAllocCopy(newname, addr);
  890.     filename=HTParse(newname, "", PARSE_PATH|PARSE_PUNCTUATION);
  891.     nodename=HTParse(newname, "", PARSE_HOST);
  892.     free(newname);
  893.  
  894.     format = HTFileFormat(filename, &encoding, WWW_PLAINTEXT, &compressed);
  895.  
  896.  
  897. #ifdef vms
  898. /* Assume that the file is in Unix-style syntax if it contains a '/'
  899.    after the leading one @@ */
  900.     {
  901.     char * vmsname = strchr(filename + 1, '/') ?
  902.       vms_name(nodename, filename) : filename + 1;
  903.     fd = open(vmsname, O_RDONLY, 0);
  904.  
  905. /*    If the file wasn't VMS syntax, then perhaps it is ultrix
  906. */
  907.     if (fd<0) {
  908.         char ultrixname[INFINITY];
  909.         if (TRACE) fprintf(stderr, "HTFile: Can't open as %s\n", vmsname);
  910.         sprintf(ultrixname, "%s::\"%s\"", nodename, filename);
  911.         fd = open(ultrixname, O_RDONLY, 0);
  912.         if (fd<0) {
  913.         if (TRACE) fprintf(stderr,
  914.                    "HTFile: Can't open as %s\n", ultrixname);
  915.         }
  916.     }
  917.     }
  918. #else
  919.  
  920.     free(filename);
  921.  
  922. /*    For unix, we try to translate the name into the name of a transparently
  923. **    mounted file.
  924. **
  925. **    Not allowed in secure (HTClienntHost) situations TBL 921019
  926. */
  927. #ifndef NO_UNIX_IO
  928.     /*    Need protection here for telnet server but not httpd server */
  929.  
  930.     {        /* try local file system */
  931.     char * localname = HTLocalName(addr);
  932.     struct stat dir_info;
  933.  
  934.     if (!localname)
  935.       goto suicide;
  936.  
  937. #ifdef GOT_READ_DIR
  938.  
  939. /*              Multiformat handling
  940. **
  941. **    If needed, scan directory to find a good file.
  942. **  Bug:  we don't stat the file to find the length
  943. */
  944.     if ( (strlen(localname) > strlen(MULTI_SUFFIX))
  945.        && (0==strcmp(localname + strlen(localname) - strlen(MULTI_SUFFIX),
  946.               MULTI_SUFFIX))) {
  947.         DIR *dp;
  948.  
  949.         STRUCT_DIRENT * dirbuf;
  950.         float best = NO_VALUE_FOUND;    /* So far best is bad */
  951.         HTFormat best_rep = NULL;    /* Set when rep found */
  952.         STRUCT_DIRENT best_dirbuf;    /* Best dir entry so far */
  953.  
  954.         char * base = strrchr(localname, '/');
  955.         int baselen;
  956.  
  957.         if (!base || base == localname) goto forget_multi;
  958.         *base++ = 0;        /* Just got directory name */
  959.         baselen = strlen(base)- strlen(MULTI_SUFFIX);
  960.         base[baselen] = 0;    /* Chop off suffix */
  961.  
  962.         dp = opendir(localname);
  963.         if (!dp) {
  964. forget_multi:
  965.         free(localname);
  966.         return HTLoadError(sink, 500,
  967.             "Multiformat: directory scan failed.");
  968.         }
  969.  
  970.         while (dirbuf = readdir(dp)) {
  971.             /* while there are directory entries to be read */
  972.         if (dirbuf->d_ino == 0) continue;
  973.                 /* if the entry is not being used, skip it */
  974.  
  975.         if (!strncmp(dirbuf->d_name, base, baselen)) {
  976.             HTFormat rep = HTFileFormat(dirbuf->d_name, &encoding,
  977.                         WWW_PLAINTEXT, &compressed);
  978.             float value = HTStackValue(rep, format_out,
  979.                         HTFileValue(dirbuf->d_name),
  980.                         0.0  /* @@@@@@ */);
  981.             if (value != NO_VALUE_FOUND) {
  982.             if (TRACE) fprintf(stderr,
  983.                 "HTFile: value of presenting %s is %f\n",
  984.                 HTAtom_name(rep), value);
  985.             if  (value > best) {
  986.                 best_rep = rep;
  987.                 best = value;
  988.                 best_dirbuf = *dirbuf;
  989.                }
  990.             }    /* if best so far */
  991.          } /* if match */
  992.  
  993.         } /* end while directory entries left to read */
  994.         closedir(dp);
  995.  
  996.         if (best_rep) {
  997.         format = best_rep;
  998.         base[-1] = '/';         /* Restore directory name */
  999.         base[0] = 0;
  1000.         StrAllocCat(localname, best_dirbuf.d_name);
  1001.         goto open_file;
  1002.  
  1003.         } else {            /* If not found suitable file */
  1004.         free(localname);
  1005.         return HTLoadError(sink, 403,    /* List formats? */
  1006.            "Could not find suitable representation for transmission.");
  1007.         }
  1008.         /*NOTREACHED*/
  1009.     } /* if multi suffix */
  1010. /*
  1011. **    Check to see if the 'localname' is in fact a directory.  If it is
  1012. **    create a new hypertext object containing a list of files and
  1013. **    subdirectories contained in the directory.  All of these are links
  1014. **    to the directories or files listed.
  1015. **    NB This assumes the existance of a type 'STRUCT_DIRENT', which will
  1016. **    hold the directory entry, and a type 'DIR' which is used to point to
  1017. **    the current directory being read.
  1018. */
  1019.  
  1020.  
  1021.     if (stat(localname,&dir_info) == -1) {       /* get file information */
  1022.                        /* if can't read file information */
  1023.         if (TRACE) fprintf(stderr, "HTFile: can't stat %s\n", localname);
  1024.  
  1025.     }  else {        /* Stat was OK */
  1026.  
  1027.  
  1028.         if (((dir_info.st_mode) & S_IFMT) == S_IFDIR) {
  1029.         /* if localname is a directory */
  1030.  
  1031. /*
  1032. **
  1033. ** Read the localdirectory and present a nicely formatted list to the user
  1034. ** Re-wrote most of the read directory code here, excepting for the checking
  1035. ** access.
  1036. **
  1037. ** Author: Charles Henrich (henrich@crh.cl.msu.edu)   10-09-93
  1038. **
  1039. ** This is still pretty messy, need to go through and clean it up at some point
  1040. **
  1041. */
  1042.  
  1043. /* Define some parameters that everyone should already have */
  1044.  
  1045. #ifndef MAXPATHLEN
  1046. #define MAXPATHLEN 1024
  1047. #endif
  1048.  
  1049. #ifndef BUFSIZ
  1050. #define BUFSIZ 4096
  1051. #endif
  1052.  
  1053.         char filepath[MAXPATHLEN];
  1054.         char buffer[4096];
  1055.  
  1056.         char *ptr;
  1057.         char *dataptr;
  1058.  
  1059.         HText * HT;
  1060.         HTFormat format;
  1061.         HTAtom *pencoding;
  1062.  
  1063.         struct stat statbuf;
  1064.         STRUCT_DIRENT * dp;
  1065.         DIR *dfp;
  1066.  
  1067.         int cmpr;
  1068.         int count;
  1069.  
  1070.         if (TRACE)
  1071.             fprintf(stderr,"%s is a directory\n",localname);
  1072.  
  1073. /*    Check directory access.
  1074. **    Selective access means only those directories containing a
  1075. **    marker file can be browsed
  1076. */
  1077.         if (HTDirAccess == HT_DIR_FORBID) {
  1078.             free(localname);
  1079.             return HTLoadError(sink, 403,
  1080.             "Directory browsing is not allowed.");
  1081.         }
  1082.  
  1083.  
  1084.         if (HTDirAccess == HT_DIR_SELECTIVE) {
  1085.             char * enable_file_name =
  1086.             malloc(strlen(localname)+ 1 +
  1087.              strlen(HT_DIR_ENABLE_FILE) + 1);
  1088.             strcpy(enable_file_name, localname);
  1089.             strcat(enable_file_name, "/");
  1090.             strcat(enable_file_name, HT_DIR_ENABLE_FILE);
  1091.             if (stat(enable_file_name, &statbuf) != 0) {
  1092.             free(localname);
  1093.             return HTLoadError(sink, 403,
  1094.             "Selective access is not enabled for this directory.");
  1095.             }
  1096.         }
  1097.  
  1098.  
  1099.         dfp = opendir(localname);
  1100.         if (!dfp) {
  1101.             free(localname);
  1102.             return HTLoadError(sink, 403, "This directory is not readable.");
  1103.         }
  1104.  
  1105. /* Suck the directory up into a list to be sorted */
  1106.  
  1107.         HTSortInit();
  1108.  
  1109.         for(dp=readdir(dfp);dp != NULL;dp=readdir(dfp))
  1110.             {
  1111.             ptr = malloc(strlen(dp->d_name)+1);
  1112.             if(ptr == NULL)
  1113.             {
  1114.             return HTLoadError(sink, 403, "Ran out of memory in directory read!");
  1115.             }
  1116.             strcpy(ptr,dp->d_name);
  1117.  
  1118.             HTSortAdd(ptr);
  1119.             }
  1120.  
  1121.         closedir(dfp);
  1122.  
  1123. /* Sort the dir list */
  1124.  
  1125.         HTSortSort();
  1126.  
  1127. /* Start a new HTML page */
  1128.  
  1129.         HT = HText_new();
  1130.         HText_beginAppend(HT);
  1131.         HText_appendText(HT, "<H1>Local Directory ");
  1132.         HText_appendText(HT, localname);
  1133.         HText_appendText(HT, "</H1>\n");
  1134.         HText_appendText(HT,"<DL>\n");
  1135.  
  1136. /* Sort the list and then spit it out in a nice form */
  1137.  
  1138. /* How this for a disgusting loop :) */
  1139.  
  1140.         for(count=0,dataptr=HTSortFetch(count);
  1141.             dataptr != NULL;
  1142.             free(dataptr), count++, dataptr=HTSortFetch(count))
  1143.             {
  1144.  
  1145. /* We dont want to see . */
  1146.  
  1147.             if(strcmp(dataptr,".") == 0) continue;
  1148.  
  1149. /* If its .. *and* the current directory is / dont show anything, otherwise
  1150. /* print out a nice Parent Directory entry.
  1151. /* */
  1152.  
  1153.             if(strcmp(dataptr,"..") == 0)
  1154.             {
  1155.             if(strcmp(localname,"/") != 0)
  1156.                 {
  1157.                 strcpy(buffer,localname);
  1158.  
  1159.                 ptr = strrchr(buffer, '/');
  1160.  
  1161.                 if(ptr != NULL) *ptr='\0';
  1162.  
  1163.                 if(buffer[0] == '\0') strcpy(buffer,"/");
  1164.  
  1165.                 HText_appendText(HT,"<DD><A HREF=\"");
  1166.                 HText_appendText(HT, buffer);
  1167.  
  1168.                 HText_appendText(HT,"\"><IMG SRC=\"");
  1169.                 HText_appendText(HT, HTgeticonname(NULL, "directory"));
  1170.  
  1171.                 HText_appendText(HT,"\"> Parent Directory</a>");
  1172.                 continue;
  1173.                 }
  1174.             else
  1175.                 {
  1176.                 continue;
  1177.                 }
  1178.             }
  1179.  
  1180. /* Get the filesize information from a stat, if we cant stat it, we probably */
  1181. /* cant read it either, so ignore it. */
  1182.  
  1183.             sprintf(filepath,"%s/%s",localname, dataptr);
  1184.  
  1185.             if(stat(filepath, &statbuf) == -1) continue;
  1186.  
  1187.             HText_appendText(HT,"<DD><A HREF=\"");
  1188.             HText_appendText (HT, localname);
  1189.  
  1190.             if(localname[strlen(localname)-1] != '/')
  1191.             {
  1192.             HText_appendText (HT, "/");
  1193.             }
  1194.  
  1195.             HText_appendText (HT, dataptr);
  1196.             HText_appendText (HT, "\">");
  1197.  
  1198. /* If its a directory, dump out a dir icon, dont bother with anything else */
  1199. /* if it is a file try and figure out what type of file it is, and grab    */
  1200. /* the appropriate icon.  If we cant figure it out, call it text.  If its  */
  1201. /* a compressed file, call it binary no matter what               */
  1202.  
  1203.             if(statbuf.st_mode & S_IFDIR)
  1204.             {
  1205.             sprintf(buffer,"%s",dataptr);
  1206.             HText_appendText(HT, "<IMG SRC=\"");
  1207.             HText_appendText(HT, HTgeticonname(NULL, "directory"));
  1208.             HText_appendText(HT, "\"> ");
  1209.             }
  1210.             else
  1211.             {
  1212.             sprintf(buffer,"%s (%d bytes)",
  1213.                 dataptr, statbuf.st_size);
  1214.  
  1215.             format = HTFileFormat(dataptr, &pencoding,
  1216.                      WWW_SOURCE, &cmpr);
  1217.  
  1218. /* If its executable then call it application, else it might as well be text */
  1219.  
  1220.             if(cmpr == 0)
  1221.                 {
  1222.                 HText_appendText(HT, "<IMG SRC=\"");
  1223.                 if((statbuf.st_mode & S_IXUSR) ||
  1224.                    (statbuf.st_mode & S_IXGRP) ||
  1225.                    (statbuf.st_mode & S_IXOTH))
  1226.                 {
  1227.                 HText_appendText(HT,
  1228.                     HTgeticonname(format, "application"));
  1229.                 }
  1230.                 else
  1231.                 {
  1232.                 HText_appendText(HT,
  1233.                     HTgeticonname(format, "text"));
  1234.                 }
  1235.                 HText_appendText(HT, "\"> ");
  1236.                 }
  1237.             else
  1238.                 {
  1239.                 HText_appendText(HT, "<IMG SRC=\"");
  1240.                 HText_appendText(HT, HTgeticonname(NULL, "application"));
  1241.                 HText_appendText(HT, "\"> ");
  1242.                 }
  1243.             }
  1244.  
  1245. /* Spit out the anchor */
  1246.  
  1247.             HText_appendText (HT, buffer);
  1248.             HText_appendText (HT, "</A>\n");
  1249.             }
  1250.  
  1251. /* End of list, clean up and we are done */
  1252.  
  1253.         HText_appendText (HT, "</DL>\n");
  1254.         HText_endAppend (HT);
  1255.         free(localname);
  1256.         return HT_LOADED;
  1257.         } /* end if localname is directory */
  1258.  
  1259.     } /* end if file stat worked */
  1260.  
  1261. /* End of directory reading section
  1262. */
  1263. #endif
  1264. open_file:
  1265.     {
  1266.         FILE * fp = fopen(localname,"r");
  1267.         if(TRACE) fprintf (stderr, "HTFile: Opening `%s' gives %p\n",
  1268.                 localname, (void*)fp);
  1269.         if (fp) {        /* Good! */
  1270.         if (HTEditable(localname)) {
  1271.             HTAtom * put = HTAtom_for("PUT");
  1272.             HTList * methods = HTAnchor_methods(anchor);
  1273.             if (HTList_indexOf(methods, put) == (-1)) {
  1274.             HTList_addObject(methods, put);
  1275.             }
  1276.         }
  1277.         free(localname);
  1278.         HTParseFile(format, format_out, anchor, fp, sink, compressed);
  1279.         fclose(fp);
  1280.         return HT_LOADED;
  1281.         }  /* If succesfull open */
  1282.     }    /* scope of fp */
  1283.     }  /* local unix file system */
  1284. #endif
  1285. #endif
  1286.  
  1287. /*    Now, as transparently mounted access has failed, we try FTP.
  1288. */
  1289.   suicide:
  1290.     return HTFTPLoad(addr, anchor, format_out, sink);
  1291. }
  1292.  
  1293. /*        Protocol descriptors
  1294. */
  1295. PUBLIC HTProtocol HTFTP  = { "ftp", HTFTPLoad, 0 };
  1296. PUBLIC HTProtocol HTFile = { "file", HTLoadFile, 0 };
  1297.